// for loger
#include <pantheios/pantheios.hpp>
#include <pantheios/inserters.hpp>

#include "shader_mgr.h"
#include <d3d9.h>
#include <iltclient.h>
#include "clientinterfaces.h"
#include <iltrendermgr.h>
#include <iltdrawprim.h>
#include <ltgraphicscaps.h>

#include "LTEffectImpl.h"
#include <d3dx9effect.h>
#include "d3d_device_wrapper.h"
#include "ltclientshell.h"
#include "pageinfogetter.hpp"
#include <boost/lexical_cast.hpp>
#include "shader/shader_loader.h"
#include "shader/shader.h"
#include "shader/shader_light_system.h"
#include "lteffectshadermgr.h"
#include "d3d_texture_manager.h"


extern IDirect3DCubeTexture9*       g_apCubeMap;
extern CDirect3DDevice9Wrapper*     g_Device9Wrapper;
extern IDirect3DDevice9*                   g_Direct3DDevice9;
extern bool loadcubemapflag;



extern IDirect3DSurface9*           g_pDepthCube;




enum ERenderTargetIDS
{
	RENDERTARGET_NONE	 = 0,
	RENDERTARGET_OVERLAY,
	RENDERTARGET_SECONDLEVEL,
	RENDERTARGET_FORCE32BITS = 0xFFFFFFFF
};




shader_manager::shader_manager(CLTClientShell* shell) :
m_if_full_screen_on(false),
m_nScreenWidth(800),
m_nScreenHeight(600),
m_shell(shell),
m_light_system(new shader_light_system),
m_d3d_textures_manager(new d3d_texture_manager)
{
    pantheios::log_DEBUG("create a new shader_manager ") ;
    m_d3d_textures_manager->load_textures_from_page("d3d_textures") ;
}

shader_manager::~shader_manager(void)
{
	for (SHADER_TYPE_COLL_IT it=m_shaders.begin();
		it!=m_shaders.end();++it)
	{
		if(*it)
		{
			delete (*it);
			(*it)=0;
		}
	}
}

void shader_manager::recreate() {
    //g_pLTCRenderMgr->RecreateEffects() ;
    m_d3d_textures_manager->recreate_textures() ;
}



LTRESULT shader_manager::init()
{
    
    pantheios::log_ALERT("init shader manager") ;
    using namespace Utility ;
    HEFFECTPOOL hEffectPool = 1;
    LTRESULT rEffectPool = g_pLTCRenderMgr->CreateEffectPool(hEffectPool);
    if(rEffectPool == LT_OK)
    {
        pantheios::log_DEBUG("SUCCESS: Effect pool ",
                             pantheios::integer(hEffectPool),
                             " Created!") ;
        g_pLTClient->CPrint("SUCCESS: Effect Pool %d Created!", hEffectPool);
    }
    else
    {
        pantheios::log_ERROR("FAILURE: EFFECT POOL CREATED FAILED") ;
        g_pLTClient->CPrint("FAILURE: Effect Pool %d Creation Failed!", hEffectPool);
    }

    pantheios::log_ALERT("before load_shader_types") ;
    
    load_shader_types();
    pantheios::log_ALERT("end load_shader_types") ;
    
    //create rendertarger;
    HSURFACE hScreenSurface = g_pLTClient->GetScreenSurface();
    g_pLTClient->GetSurfaceDims(hScreenSurface, &m_nScreenWidth, &m_nScreenHeight);

    if(g_pLTCRenderMgr->CreateRenderTarget(m_nScreenWidth, m_nScreenHeight, RTFMT_X8R8G8B8, STFMT_UNKNOWN, RENDERTARGET_OVERLAY) != LT_OK)
    {
        pantheios::log_ERROR("FAILED to create rendertarget of ",
                             pantheios::integer(RENDERTARGET_OVERLAY), " : ",
                             pantheios::integer(m_nScreenWidth),
                             " X ",
                             pantheios::integer(m_nScreenHeight)) ;
        g_pLTClient->CPrint("Failed to create rendertarget of 320x240");
    }

    if(g_pLTCRenderMgr->CreateRenderTarget(m_nScreenWidth, m_nScreenHeight, RTFMT_X8R8G8B8, STFMT_UNKNOWN, RENDERTARGET_SECONDLEVEL) != LT_OK)
    {
         pantheios::log_ERROR("FAILED to create rendertarget of ",
                             pantheios::integer(RENDERTARGET_SECONDLEVEL), " : ",
                             pantheios::integer(m_nScreenWidth),
                             " X ",
                             pantheios::integer(m_nScreenHeight)) ;
       
        g_pLTClient->CPrint("Failed to create rendertarget of 320x240");
    }
    pantheios::log_ALERT("haha ok here ?????????????");
    
	
    g_Device9Wrapper = g_pLTClient->GetD3DWrapper();
    g_Direct3DDevice9 = g_Device9Wrapper->GetDevice();
    return LT_OK;
}




//Pixel/Vertex shader defines. Taken from d3d9types.h.

// pixel shader version token
#define PS_VERSION(_Major,_Minor) (0xFFFF0000|((_Major)<<8)|(_Minor))

// vertex shader version token
#define VS_VERSION(_Major,_Minor) (0xFFFE0000|((_Major)<<8)|(_Minor))

uint32 g_nPixelShaderMajor = 0;
uint32 g_nPixelShaderMinor = 0;
uint32 g_nVertexShaderMajor = 0;
uint32 g_nVertexShaderMinor = 0;

LTRESULT shader_manager::query_device_caps()
{
    LTGraphicsCaps caps;
    LTRESULT res = g_pLTClient->QueryGraphicDevice(&caps);
    if(res == LT_OK)
    {
        g_pLTClient->CPrint(" ");
        g_pLTClient->CPrint("QueryGraphicDevice:");
        g_pLTClient->CPrint("-------------------------------------");
        if(caps.PixelShaderVersion < PS_VERSION(1,0))
        {
            g_pLTClient->CPrint("Warning: Pixel Shader version less than 1.0!");
        }
        else
        {
            g_pLTClient->CPrint("Graphic device supports Pixel Shader version 1.0 or greater.");
            g_nPixelShaderMajor = 1;
        }

        if(caps.PixelShaderVersion < PS_VERSION(1,1))
        {
            g_pLTClient->CPrint("Warning: Pixel Shader version less than 1.1!");
        }
        else
        {
            g_pLTClient->CPrint("Graphic device supports Pixel Shader version 1.1 or greater.");
            g_nPixelShaderMajor = 1;
            g_nPixelShaderMinor = 1;
        }

        if(caps.PixelShaderVersion < PS_VERSION(2,0))
        {
            g_pLTClient->CPrint("Warning: Pixel Shader version less than 2.0!");
        }
        else
        {
            g_pLTClient->CPrint("Graphic device supports Pixel Shader version 2.0 or greater.");
            g_nPixelShaderMajor = 2;
        }

        if(caps.PixelShaderVersion < PS_VERSION(3,0))
        {
            g_pLTClient->CPrint("Warning: Pixel Shader version less than 3.0!");
        }		
        else
        {
            g_pLTClient->CPrint("Graphic device supports Pixel Shader version 3.0 or greater.");
            g_nPixelShaderMajor = 3;
        }

        if(caps.VertexShaderVersion < VS_VERSION(1,0))
        {
            g_pLTClient->CPrint("Warning: Vertex Shader version less than 1.0!");
        }
        else
        {
            g_pLTClient->CPrint("Graphic device supports Vertex Shader version 1.0 or greater.");
        }

        if(caps.VertexShaderVersion < VS_VERSION(1,1))
        {
            g_pLTClient->CPrint("Warning: Vertex Shader version less than 1.1!");
        }
        else
        {
            g_pLTClient->CPrint("Graphic device supports Vertex Shader version 1.1 or greater.");
            g_nVertexShaderMajor = 1;
            g_nVertexShaderMinor = 1;
        }

        if(caps.VertexShaderVersion < VS_VERSION(2,0))
        {
            g_pLTClient->CPrint("Warning: Vertex Shader version less than 2.0!");
        }
        else
        {
            g_pLTClient->CPrint("Graphic device supports Vertex Shader version 2.0 or greater.");
            g_nVertexShaderMajor = 2;
        }

        if(caps.VertexShaderVersion < VS_VERSION(3,0))
        {
            g_pLTClient->CPrint("Warning: Vertex Shader version less than 3.0!");
        }
        else
        {
            g_pLTClient->CPrint("Graphic device supports Vertex Shader version 3.0 or greater.");
            g_nVertexShaderMajor = 3;
        }

        g_pLTClient->CPrint("-------------------------------------");
    } else {
        pantheios::log_ERROR("Querry device error") ;
        g_pLTClient->CPrint("Error: Could not query the device caps!");
        return LT_ERROR;
    }

    return LT_OK;


}

void shader_manager::on_effect_shader_set_params( LTEffectShader *pEffect, CRenderStyle *pRenderStyle, HOBJECT hObj, const LTShaderDeviceState &ShaderDeviceState )
{
    static int effect_id = 0 ;
    int old_eff = 0 ;
	for (SHADER_TYPE_COLL_IT it=m_shaders.begin();
		it!=m_shaders.end();++it)
	{
		(*it)->set_args(hObj,pEffect,ShaderDeviceState);
	}
}

LTRESULT shader_manager::add_effect_shader( const char *pFileName, int EffectShaderID, const uint32 *pVertexElements, uint32 VertexElementsSize, HEFFECTPOOL EffectPoolID )
{
    pantheios::log_ALERT("add_effect_shader is caling...... ", pFileName, " ",
                         pantheios::integer(EffectShaderID)) ;
	LTRESULT bRet = g_pLTCRenderMgr->AddEffectShader(pFileName, EffectShaderID, pVertexElements, VertexElementsSize, EffectPoolID);
  pantheios::log_ALERT(" what's wrong.........") ;
  
	if(bRet == LT_OK)
	{
		g_pLTClient->CPrint("Effect Shader %s is good...", pFileName);

		//find a valid technique
		LTEffectShader* pEffect = g_pLTCRenderMgr->GetEffectShader(EffectShaderID);
		if(pEffect)
		{
			LTTechniqueInfo info;
			if(pEffect->FindFirstValidTechnique(&info) == LT_OK)
			{
				g_pLTClient->CPrint("    Found valid technique: %s", info.szName);
				if(pEffect->SetTechnique(info.szName) == LT_OK)
				{
					g_pLTClient->CPrint("    Using technique: %s", info.szName);
				}
			}
		}
	}
	else
	{
		g_pLTClient->CPrint("(*!!!*) Effect Shader %s failed to load!!!", pFileName);
	}

	return bRet;


}

void shader_manager::update( float fFrameTime, bool bRendering )
{
    
	return;
}

void shader_manager::render( float fFrameTime )
{
	if (!m_if_full_screen_on)
	{
		return;
	}
	switch(m_current_full_fx)
	{
	case SHADER_FULL_MOTION_BLUR:
		{
        render_full_screen_effect(fFrameTime,SHADER_FULL_MOTION_BLUR);	
		}
		break;
	default:
		break;
	}

}



LTRESULT shader_manager::render_full_screen_effect( float fFrameTime, uint32 nID )
{
   // pantheios::log_DEBUG("render_full_screen_effect") ;
    
	//if(nID != 9)//hdr
	{
      
      HRESULT hr ;
      
		hr = g_pLTCRenderMgr->StoreDefaultRenderTarget();

		if(g_pLTCRenderMgr->InstallRenderTarget(RENDERTARGET_OVERLAY) != LT_OK)
		{
			g_pLTClient->CPrint("Failed to InstallRenderTarget");
		}
      
		//	Tell LithTech to render the scene using this camera
		LTRESULT result = g_pLTClient->RenderCamera(m_hCamera, fFrameTime);
        m_shell->render_for_shader() ;
		if (LT_OK != result)
		{
			g_pLTClient->DebugOut("Error in RenderCamera()\n");
			g_pLTClient->Shutdown();
		}
   

		g_pLTCRenderMgr->RestoreDefaultRenderTarget();
	}

	//if(nID == 9)//motion blur
	{
		g_pLTCRenderMgr->RestoreDefaultRenderTarget();
	}

	if(true)
	{
		float top 		= 0.0f - 0.05f; //0.05 needed to correct the offset
		float bottom 	= (float)m_nScreenHeight;
		float left 		= 0.0f - 0.05f; //0.05 needed to correct the offset
		float right 	= (float)m_nScreenWidth;

		LT_POLYFT4 poly;
		poly.verts[0].x = left;
		poly.verts[0].y = top;
		poly.verts[0].z = 0.0f;
		poly.verts[0].u = 0.0f;
		poly.verts[0].v = 0.0f;

		poly.verts[1].x = right;
		poly.verts[1].y = top;
		poly.verts[1].z = 0.0f;
		poly.verts[1].u = 1.0f;
		poly.verts[1].v = 0.0f;

		poly.verts[2].x = right;
		poly.verts[2].y = bottom;
		poly.verts[2].z = 0.0f;
		poly.verts[2].u = 1.0f;
		poly.verts[2].v = 1.0f;

		poly.verts[3].x = left;
		poly.verts[3].y = bottom;
		poly.verts[3].z = 0.0f;
		poly.verts[3].u = 0.0f;
		poly.verts[3].v = 1.0f;

		// Set up color and alpha.
		poly.rgba.r = 255;
		poly.rgba.g = 255;
		poly.rgba.b = 255;
		poly.rgba.a = 255;

		// Set up the drawprim render state.
		g_pLTCDrawPrim->SetTransformType(DRAWPRIM_TRANSFORM_SCREEN);
		g_pLTCDrawPrim->SetColorOp(DRAWPRIM_MODULATE);
		//g_pLTCDrawPrim->SetAlphaBlendMode(DRAWPRIM_NOBLEND);
		g_pLTCDrawPrim->SetAlphaBlendMode(DRAWPRIM_BLEND_MOD_SRCALPHA);
		g_pLTCDrawPrim->SetZBufferMode(DRAWPRIM_NOZ);
		g_pLTCDrawPrim->SetAlphaTestMode(DRAWPRIM_NOALPHATEST);
		g_pLTCDrawPrim->SetClipMode(DRAWPRIM_FASTCLIP);
		g_pLTCDrawPrim->SetFillMode(DRAWPRIM_FILL);
		g_pLTCDrawPrim->SetCullMode(DRAWPRIM_CULL_NONE);
		g_pLTCDrawPrim->SetCamera(m_hCamera);
		g_pLTCDrawPrim->SetEffectShaderID(nID);


		// Draw the Image (just 1 quad).
		g_pLTCDrawPrim->DrawPrim(&poly, 1);

		g_pLTCDrawPrim->SetZBufferMode( DRAWPRIM_ZRW);
		g_pLTCDrawPrim->SetAlphaBlendMode(DRAWPRIM_BLEND_MOD_SRCALPHA);
		g_pLTCDrawPrim->SetCamera(NULL);
		g_pLTCDrawPrim->SetEffectShaderID(NULL);
	


	}

	
	return LT_OK;

}

void shader_manager::set_mode( ShaderIDS eMode )
{
	m_current_full_fx=eMode;
	if (m_current_full_fx>SHADER_FULL_NONE_NULL)
	{
		m_if_full_screen_on=true;
	}

}

void shader_manager::load_shader_types()
{
    pantheios::log_ALERT("load_shader_types is calling") ;
	shader_loader::get_single().load_shaders();
  pantheios::log_ALERT("end load..............") ;
	m_shaders=shader_loader::get_single().get_coll();
	for (SHADER_TYPE_COLL_IT it=m_shaders.begin();
		it!=m_shaders.end();++it)
	{
		add_effect_shader((*it)->get_file_name().c_str(),(*it)->get_id(),(uint32*)(*it)->get_vertex_element(),
			(*it)->get_vertex_size(),NO_EFFECT_POOL);
	}

}

bool shader_manager::bind_seters(ShaderIDS _shaderID,arg_seter* _seter )
{
	if (_seter )
	{ 
		for (SHADER_TYPE_COLL_IT it=m_shaders.begin();
			it!=m_shaders.end();++it)
		{
			if ((*it)->get_id()==_shaderID)
			{
				(*it)->add_seter(_seter);
				return true;
			}
		}
	}
	return false;
}
